#! /usr/bin/env python

#
# Author: Gou Pengfei
#

import sys
import os
import optparse
import datetime
import re
from subprocess import call
from os.path import isdir
from os.path import join as joinpath
# import numpy as np

optparser = optparse.OptionParser()
optparser.add_option('-f', '--conf', dest='configuration',
                     default='distributed-base',
                     help='Configuration of this run' )

optparser.add_option('-v', '--variant', dest='var',
                     default='opt',
                     help='Binary type of M5' )

optparser.add_option('-m', '--multi-system', action='store_true',
                     dest='multisystem',
                     default=False,
                     help='Are we a multi-system simulation?' )
                     
(options,leftovers) = optparser.parse_args()

# regular expressions of lines to ignore when diffing outputs
output_ignore_regexes = (
    '^command line:',       # for stdout file
    '^M5 compiled ',        # for stderr file
    '^M5 started ',     # for stderr file
    '^M5 executing on ',    # for stderr file
    '^Simulation complete at',  # for stderr file
    '^Listening for',       # for stderr file
    'listening for remote gdb', # for stderr file
    '^M5 Simulator System', # for stdout file
    '^Copyright',
    '^The Regents of The University of Michigan',
    '^All Rights Reserved',
    '^Global frequency',
    '^info:',
    '^Exiting @',
    '^M5 revision'
    )

output_ignore_args = ' '.join(["-I '"+s+"'" for s in output_ignore_regexes])
output_ignore_args += ' -B'

home = os.environ['HOME']

bench_dir = joinpath(home, 'ttools2-devel/bin/')
output_dir = joinpath(home, 'workdir/simplebench/%s' %
                      (options.configuration))

if not isdir(output_dir):
    os.makedirs(output_dir)

tsim_cmd = '%s/ttools2-devel/sbin/tsim_arch -nologout -s -out\
            %s/tsim %s > %s/simout'
m5_cmd = '%s/M5-EDGE/build/TRIPS_SE/m5.%s --outdir=%s/%s/m5 --stats-file\
            stats.txt -re %s/M5-EDGE/configs/trips_config/%s.py --simplebench %s'
            
diff_cmd = 'diff -ub %s %s %s > %s'

status_template = '%s\t\t%s\t\t%s\t\t%f\t\t%f\t\t%f%%\t\t%s'

# This two programs need keyboard input, so ignore them
excluded_progs = ['dt_scanf.tst','dt_string1.tst']
                  
class Stat(object):
    stat = float(0)
    bname = ''
    sname = ''
    stats_file = ''
    def __init__(self,bname,config,fname,**kwargs):
        self.bname = bname
        self.stats_file = output_dir + '/' + bname + '/' + config + '/'  +  fname

    def data(self,sname):
        for line in open(self.stats_file):
            l = line.split()
            if not l:
                continue
            if l[0] == sname:
                self.stat = float(l[1])
                break

        return self.stat
        
# Call os.system() and raise exception if return status is non-zero
def system(cmd):
    retcode = call(cmd, shell=True)
    if retcode < 0:
        print >>sys.stderr, "Child was terminated by signal", -retcode
        print >>sys.stderr, "When attemping to execute: %s" % cmd
    elif retcode > 0:
        print >>sys.stderr, "Child returned", retcode
        print >>sys.stderr, "When attemping to execute: %s" % cmd

    return retcode

benchmarks_to_run = os.listdir(bench_dir)

# Remove some unwanted programs
for prog in excluded_progs:
    benchmarks_to_run.remove(prog)

# benchmarks_to_run.remove('.svn')

# Open a file for status output
status_file = file(output_dir + '/status','w')
print >>status_file,'Name\t\tRun status\t\tStandard Output\t\tCommitted blocks\t\tStatus'
print >> status_file,'\t\t\t\t\t\t\t--- m5_edge -- tsim_arch ---diff'

failed_count = 0
passed_count = 0
overall_count = 0
for bench in benchmarks_to_run:
    bench_name = (bench.split('.'))[0]
    print
    print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
    print ">>>>>>>>>>>>>>>>>> %s >>>>>>>>>>>>>>>>>>>" % bench_name

    tsim_dir = output_dir + '/' + bench_name + '/' + 'tsim'

    if not isdir(tsim_dir):
        os.makedirs(tsim_dir)

    m5_run_status = system(m5_cmd % (home, options.var, output_dir, bench_name, home, options.configuration,bench))    
    tsim_run_status = system(tsim_cmd % (home, tsim_dir, bench_dir + bench, tsim_dir))

    run_status_string = ''
    if m5_run_status != 0 or tsim_run_status != 0:
        run_status_string = 'WRONG'
    else:
        run_status_string = 'OK'

    tsim_out = tsim_dir + '/' + 'simout'
    m5_out = output_dir + '/' + bench_name + '/m5' + '/output'
    diff_out = output_dir + '/' + bench_name + '/diffout'
    diffcmd = diff_cmd %(output_ignore_args,m5_out,tsim_out,diff_out)
    
    # Compare the standard output
    output_status = call(diffcmd,shell=True)
    output_status_string = ''
    status_string = ''
    if output_status == 0:
        print 'Outputs of %s are the same for tsim_arch and m5_edge' % bench_name
        output_status_string = 'OK'
    else:
        print 'Outputs of %s are different for tsim_arch and m5_edge' % bench_name
        output_status_string = 'Not OK'
        
    # Compare the number of committed blocks
    m5_stat = Stat(bench_name,'m5','stats.txt')
    tsim_stat = Stat(bench_name,'tsim','tsim.stats')

    if not options.multisystem :
        m5_blocks = m5_stat.data('system.cpu.commit.COM:count')
    else :
        m5_blocks = m5_stat.data('system0.cpu.commit.COM:count')
    tsim_blocks = tsim_stat.data('sim.blocks')

    if m5_blocks == tsim_blocks:
        if output_status == 0:
            status_string = 'PASSED'
            passed_count+=1
        else:
            status_string = 'FAILED'
            failed_count+=1
            
        print 'Number of committed blocks of %s is the same for tsim_arch and m5_edge' % bench_name
    else:
        status_string = 'FAILED'
        failed_count+=1
        print 'For %s: Committed blocks of tsim_arch is %f while m5_edge is %f' % \
            (bench_name,tsim_blocks,m5_blocks)

    overall_count+=1
    print >>status_file,status_template % \
        (bench_name,run_status_string,output_status_string,m5_blocks,tsim_blocks,\
        (m5_blocks-tsim_blocks)*100/tsim_blocks,status_string)

print >>status_file
print >>status_file
print >>status_file,'''Number of progs run:%i
Number of passed progs:%i
Number of failed progs:%i''' % (overall_count,passed_count,failed_count)

status_file.close()

sys.exit(0)

